package cc.shacocloud.mirage.core.impl;

import cc.shacocloud.mirage.bean.ConditionHandler;
import cc.shacocloud.mirage.bean.ConfigurableBeanFactory;
import cc.shacocloud.mirage.bean.meta.BeanKey;
import cc.shacocloud.mirage.bean.meta.FactoryPoint;
import cc.shacocloud.mirage.core.Condition;
import cc.shacocloud.mirage.core.ConditionContext;
import cc.shacocloud.mirage.core.ConfigurableApplicationContext;
import cc.shacocloud.mirage.core.bind.Conditional;
import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementMetadata;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import cc.shacocloud.mirage.utils.map.ConcurrentReferenceHashMap;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.AnnotatedElement;
import java.util.Map;
import java.util.Objects;

/**
 * 基于条件注解 {@link Conditional} 实现的 {@link ConditionHandler} 处理器
 *
 * @author 思追(shaco)
 * @see Conditional
 * @see Condition
 */
@Slf4j
public class ApplicationAnnotationConditionHandler implements ConditionHandler {
    
    // Condition 的缓存，用于避免 Condition 被重复创建
    private static final Map<Class<? extends Condition>, Condition> CONDITION_CACHE = new ConcurrentReferenceHashMap<>(64);
    protected final ConfigurableApplicationContext applicationContext;
    
    public ApplicationAnnotationConditionHandler(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    
    @Override
    public boolean matches(@NotNull ConfigurableBeanFactory beanFactory, @NotNull BeanKey beanKey) {
        return doMatches(beanKey, beanKey.getBeanClass());
    }
    
    @Override
    public boolean matches(@NotNull ConfigurableBeanFactory beanFactory, @NotNull FactoryPoint factoryPoint) {
        return doMatches(factoryPoint.getReturnType(), factoryPoint.getMethod());
    }
    
    /**
     * 匹配指定的注解元素上是否存在{@link Conditional}，如果不存在则默认返回 true，
     * 如果存在则根据对应的 {@link Condition} 判断结果
     *
     * @param beanKey          当前需要添加到对象工厂的 {@link BeanKey}
     * @param annotatedElement 注解元素
     */
    protected boolean doMatches(BeanKey beanKey, AnnotatedElement annotatedElement) {
        final AnnotatedElementMetadata annotatedMetadata = AnnotatedElementUtils.getAnnotatedMetadata(annotatedElement);
        Conditional conditional = annotatedMetadata.getAnnotation(Conditional.class);
        if (Objects.isNull(conditional)) return true;
        
        // 构建条件上下文
        final ConditionContext conditionContext = getConditionContext(beanKey);
        
        Class<? extends Condition>[] conditionClasses = conditional.value();
        for (Class<? extends Condition> conditionClass : conditionClasses) {
            
            Condition condition = getCondition(conditionClass);
            
            // 有一个条件不匹配则返回不匹配
            boolean matches = condition.matches(conditionContext, annotatedMetadata);
            if (!matches) return false;
        }
        
        return true;
    }
    
    /**
     * 获取当前的条件上下文
     *
     * @param beanKey 当前需要添加到对象工厂的 {@link BeanKey}
     */
    protected ConditionContext getConditionContext(BeanKey beanKey) {
        return new ApplicationConditionContextImpl(applicationContext, beanKey);
    }
    
    /**
     * 初始化条件类
     */
    protected Condition getCondition(Class<? extends Condition> conditionClass) {
        return CONDITION_CACHE.computeIfAbsent(conditionClass, ClassUtil::instantiateClass);
    }
    
}
